package com.wn.gateway.filter;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wn.common.constant.redis.RedisConstant;
import com.wn.common.jwt.JwtTemplate;
import com.wn.common.result.Result;
import com.wn.common.result.enums.impl.ResultCode;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * @author yellow Docter
 * @date 2023 -02-27
 * @desc  GlobalFilter   全局过滤器   可以对请求进行拦截
 *
 *  AuthFilter 认证的过滤器  (用户是否认证)
 *
 */
@Component
@Slf4j
public class AuthFilter implements GlobalFilter {

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * gateway 是基于 webflux  ( 和mvc完全不一样 异步编程)
     *      不要纠结语法
     *
     * @param exchange the current server exchange
     * @param chain provides a way to delegate to the next filter
     * @return
     *
     * ServerWebExchange命名为服务网络交换器，存放着重要的请求-响应属性、请求实例和响应实例等等
     * GatewayFilterChain 官网过滤对象   当前的过滤器是否放行
     *      chain.filter(exchange) 放行
     *
     *   Mono<Void>  返回的类型  (什么都没有 )
     *
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //01 拿到 请求的url 来判断
        String path = request.getURI().getPath();
        System.out.println("path = " + path);

        AntPathMatcher matcher = new AntPathMatcher();

        //02、可以对指定的url进行 校验
        //请求中携带 auth的路径 需要认证才可以访问
        boolean match = matcher.match("/**/auth/**", path);
        if (match){
            //03 拿到头部的token
            String token = request.getHeaders().getFirst("Authorization");
            System.out.println("token = " + token);
            if (StringUtils.isEmpty(token)){
                log.error("没有携带token");
                return error(response);
            }
            try {
                //04 拿到token   去掉 Bearer
                token = token.replaceAll("Bearer ","");
                //05 验证token
                boolean verifyToken = jwtTemplate.verifyToken(token);
                if (!verifyToken){
                    log.error("token数据非法...");
                    return error(response);
                }
                //06 解析token
                Long userId = (Long) jwtTemplate.parseToken(token, "userId");
                String phone = (String) jwtTemplate.parseToken(token, "phone");
                System.out.println("userId = " + userId);
                System.out.println("phone = " + phone);

                //07 判断 redis中的token是否存在 (token 是否过期没有)
                String redisToken = stringRedisTemplate.opsForValue().get(RedisConstant.USER_REDIS_TOKEN + userId);

                if (StringUtils.isEmpty(redisToken)) {  //判断是否为null
                    log.error("token已经过期...");
                    return error(response);
                }

                //08 重写请求头，把userId放入请求头中；这样各个微服务就可以从请求头中获取认证的userId
                ServerHttpRequest.Builder mutate = request.mutate();
                mutate.header("userId",userId.toString());
                mutate.header("phone",phone);

                //09 redis的token需要续命
                //redis 中的token 更新到30min
                stringRedisTemplate.expire(RedisConstant.USER_REDIS_TOKEN + userId, Duration.ofDays(30));

            }catch (Exception e){
                e.printStackTrace();
                return error(response);
            }
        }
        return chain.filter(exchange);
    }

    // 自动处理检查异常的注解
    @SneakyThrows
    private Mono<Void> error(ServerHttpResponse response)  {
        //回写异常 信息
        Result<Object> result = Result.fail(ResultCode.PERMISSION_NO_ACCESS);
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = objectMapper.writeValueAsBytes(result);
        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(dataBuffer));
    }

}
